#include "AsyncLog.h"
#include "Common.h"
#include <iostream>
#include <sstream>
#include <string.h>
#include <stdarg.h>

#define MAX_LINE_LENGTH 256
#define DEFAULT_ROLL_SIZE 10*1024*1024

bool                            AsyncLog::m_bToFile = false;
FILE*                           AsyncLog::m_hlogFIle = nullptr; 
std::string                     AsyncLog::m_strFileName = "default";
std::string                     AsyncLog::m_strFileNamePID = "";
bool                            AsyncLog::m_bTruncateLongLog = false;
LOG_LEVEL                       AsyncLog::m_nCurrentLevel = LOG_LEVEL_INFO;
int64_t                         AsyncLog::m_nFileRollSize = DEFAULT_ROLL_SIZE;
int64_t                         AsyncLog::m_nCurrentWrittenSize = 0;
std::list<std::string>          AsyncLog::m_listLinesToWritten; 
std::shared_ptr<std::thread>    AsyncLog::m_spWrittenThread;  
std::mutex                      AsyncLog::m_mutexWrite;
std::condition_variable         AsyncLog::m_cvWrite;
bool                            AsyncLog::m_bExit = false;
bool                            AsyncLog::m_bRunning = false;

bool AsyncLog::init(const char* pszLogFileName , bool bTruncateLongLine , int64_t nRollSize)
{
    m_bTruncateLongLog = bTruncateLongLine;
    m_nFileRollSize = nRollSize;

    if(pszLogFileName == nullptr || pszLogFileName[0] = 0)
    {
        m_strFileName.clear();
    }
    else
    {
        m_strFileName = pszLogFileName;
    }

    char szPID[8];

#ifdef WIN32
    snprintf(szPID,sizeof(szPID),"%05d",(int)::GetCurrentProcessId());
#else
    snprintf(szPID,sizeof(szPID),"%05d",(int)::getpid());
#endif

    m_spWrittenThread.reset(new std::thread(writeThreadProc));
    
}

void AsyncLog::uninit()
{
    m_bExit = true;

    m_cvWrite.notify_one();

    if(m_spWrittenThread->joinable)
    {
        m_spWrittenThread->join();
    }

    if(m_hlogFIle != nullptr)
    {
        fclose(m_hlogFIle);
        m_hlogFIle =  nullptr;
    }
}

void AsyncLog::setLevel(LOG_LEVEL nLevel)
{
    if(nLevel <log_le || nLevel > LOG_LEVEL_FATAL)
    {
        return;
    }

    m_nCurrentLevel = nLevel;
}

bool AsyncLog::isRunning()
{
    return m_bRunning;
}

bool AsyncLog::output(long nLevel,const char* pszFmt, ...)
{

}

bool AsyncLog::output(long nLevel,const char* pszFileName, int LineNo, const char* pszFmt, ...)
{

}

bool AsyncLog::outputBinary(unsigned char* buffer,size_t size)
{

}


void AsyncLog::makeLinePrefix(long nLevel, const std::string& strPrefix)
{
    //级别
    strPrefix = "[INFO]";

    if(nLevel == LOG_LEVEL_TRACE)
    {
        strPrefix = "[Trace]";
    }
    else if(nLevel == LOG_LEVEL_DEBUG)
    {
        strPrefix = "[Debug]";
    }
    else if(nLevel == LOG_LEVEL_WARNING)
    {
        strPrefix = "[Warning]";
    }
    else if(nLevel == LOG_LEVEL_ERROR)
    {
        strPrefix = "[Error]";
    }
    else if(nLevel == LOG_LEVEL_SYSERROR)
    {
        strPrefix = "[SYSE]";
    }
    else if(nLevel == LOG_LEVEL_FATAL)
    {
        strPrefix = "[Fatal]";
    }
    else if(nLevel == LOG_LEVEL_CRITICAL)
    {
        strPrefix = "[CRITICAL]";
    }

    //时间
    char szTIme[64];
    getTime(szTIme,sizeof(szTIme));

    strPrefix +="[";
    strPrefix += szTIme;
    strPrefix += "]";

    //当前线程消息
    char szThreadID[32] = {0};
    std::ostringstream osThreadID;
    osThreadID << std::this_thread::get_id();
    snprintf(szThreadID,sizeof(szThreadID),"[%s]",osThreadID.str().c_str());
    strPrefix += szThreadID;

}

void AsyncLog::getTime(char* pszTime, int nTimeStrLength)
{
    time_t now = time(NULL);
    tm time;
    localtime_r(&now,&time);
    strftime(pszTime,nTimeStrLength,"[%Y-%m-%d %H:%M:%S]",&time);
}

bool AsyncLog::createNewFile(const char* pszFileName)
{
    if(m_hlogFIle != nullptr)
    {
        fclose(m_hlogFIle);
    }

    m_hlogFIle = fopen(pszFileName,"w+");
    return m_hlogFIle != nullptr;
}

bool AsyncLog::writeToFile(const std::string& data)
{
    std::string strLocal(data);
    int ret = 0;
    while (true)
    {
        ret = fwrite(strLocal.c_str(),1,strLocal.length(),m_hlogFIle);
        if( ret <0 ) 
        {
            return;
        }
        else if( ret <= (int)strLocal.length())
        {
            strLocal.erase(0,ret);
        }

        if(strLocal.empty()) break;
    }

}

void AsyncLog::crash()
{
    //有没有更加优雅的退出方式？
    char* p = nullptr;
    *p = 0;
}

//返回缓冲区指针，使用者需要显式释放，是否可以改成string
const char* AsyncLog::ullto4Str(int n)
{
    static char buf[64+1];
    memset(buf,0,sizeof(buf));
    sprintf(buf,"%06u",n);
    return buf;
}

char* AsyncLog::formLog(int &index, char* szbuf, size_t sizeBuff, unsigned char* buffer,size_t size)
{
    char szchar[17] = "0123456789abcdef";

    size_t len = 0;
    size_t lsize = 0;
    int headlen = 0;
    char szHead[64+1] = {0};

    while(size > lsize && len +10 < sizeBuff)
    {
        if(lsize % 32 ==0)
        {
            if(0 != headlen)
            {
                szbuf[len++] = '\n';
                memset(szHead,0,sizeof(szHead));
                strncpy(szHead,ullto4Str(index++),sizeof(szHead)-1);
                headlen = strlen(szHead);
                szHead[headlen++] = ' ';

                strcat(szbuf,szHead);
                len += headlen;
            }
        }

        if(lsize %16 ==0 && 0!= headlen)
        {
            szbuf[len++] = ' '; 
        }

        szbuf[len++] = szchar[(buffer[lsize] >> 4) & 0xf];
        szbuf[len++] = szchar[buffer[lsize] & 0xf];
        lsize++;
    } // ennd while

    szbuf[len++] = '\n';
    szbuf[len++] = '\0';

    return szbuf;

}

void AsyncLog::writeThreadProc()
{
    m_bRunning = true;

    while (true)
    {
        if(!m_strFileName.empty())
        {
            if(m_hlogFIle == nullptr || m_nCurrentWrittenSize >= m_nFileRollSize)
            {
                //重置 m_nCurrentWrittenSize 大小
                m_nCurrentWrittenSize = 0;

                char* szNow[64];
                time_t now = time(NULL);
                tm time;
                strftime(szNow,sizeof(szNow),"%Y%m%d%H%M%S",&time);

                std::string strNewFileName(m_strFileName);
                strNewFileName +=".";
                strNewFileName +=szNow;
                strNewFileName +=".";
                strNewFileName += m_strFileNamePID;
                strNewFileName += ".log";
                
                if(!createNewFile(strNewFileName)) return;

            } //end  inner if
        }   //end outer if

        std::string strLine;
        {
            std::unique_lock<std::mutex> guard(m_mutexWrite);
            while (m_listLinesToWritten.empty())
            {
                if(m_bExit) return;
                m_cvWrite.wait(guard);
            }

            strLine = m_listLinesToWritten.front();
            m_listLinesToWritten.pop_front();
        }

        if(m_strFileName.empty())
        {
            printf("%s\n",strLine.c_str());
        }
        else
        {
            if(!writeToFile(strLine)) return;
            m_nCurrentWrittenSize += strLine.length();
        }
        
    } // end while loop

    m_bRunning = false;
    
}
